﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;

/*----------------------------------------------------------------
* 描述 ：PropertyEmit
* 作    者 ：Tengsheng Hou
* 更新时间 ：2019/7/26 13:06:49
//----------------------------------------------------------------*/
namespace Core.Toolkit.BaseLibrary.Emit
{
    public class PropertyEmit
    {

        private PropertySetterEmit setter;
        private PropertyGetterEmit getter;
        public String PropertyName { get; private set; }
        public PropertyInfo Info { get; private set; }

        public PropertyEmit(PropertyInfo propertyInfo)
        {
            Init(propertyInfo);
        }

        public PropertyEmit(Type type, string PropertyName)
        {
            PropertyInfo propertyInfo = type.GetProperty(PropertyName);
            Init(propertyInfo);
        }

        public void Init(PropertyInfo propertyInfo)
        {
            if (propertyInfo == null)
            {
                throw new ArgumentNullException("属性不能为空");
            }

            if (propertyInfo.CanWrite)
            {
                setter = new PropertySetterEmit(propertyInfo);
            }

            if (propertyInfo.CanRead)
            {
                getter = new PropertyGetterEmit(propertyInfo);
            }

            this.PropertyName = propertyInfo.Name;
            this.Info = propertyInfo;
        }


        /// <summary>
        /// 属性赋值操作（Emit技术）
        /// </summary>
        /// <param name="instance"></param>
        /// <param name="value"></param>
        public void SetValue(Object instance, Object value)
        {
            this.setter?.Invoke(instance, value);
        }

        /// <summary>
        /// 属性取值操作(Emit技术)
        /// </summary>
        /// <param name="instance"></param>
        /// <returns></returns>
        public Object GetValue(Object instance)
        {
            return this.getter?.Invoke(instance);
        }

        private static readonly ConcurrentDictionary<Type, PropertyEmit[]> securityCache = new ConcurrentDictionary<Type, PropertyEmit[]>();

        /// <summary>
        /// 获取对象属性
        /// </summary>
        /// <param name="type">对象类型</param>
        /// <returns></returns>
        public static PropertyEmit[] GetProperties(Type type)
        {
            return securityCache.GetOrAdd(type, t => t.GetProperties().Select(p => new PropertyEmit(p)).ToArray());
        }


        /// <summary>
        /// 可以优化，把PropertyEmit缓存起来
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        public static object GetValue(object obj, string propertyName)
        {
            //分割属性
            object cuurObj = obj;
            var conlumnLevel = propertyName.Split('.');
            foreach (var item in conlumnLevel)
            {
                var property = cuurObj.GetType().GetProperty(item);
                cuurObj = new PropertyEmit(property).GetValue(cuurObj);
            }
            return cuurObj;
        }
    }
}
